{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Idiomatic Python - miscellaneous part 2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## String concatenation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "names = ('John', 'Lisa', 'Terminator', 'Python')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color='red'>Don't do this.</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "semicolon_separated = names[0]\n",
    "for name in names[1:]:\n",
    "    semicolon_separated += ';' + name\n",
    "print(semicolon_separated)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### <font color='green'>Use `join` instead!</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "semicolon_separated = ';'.join(names)\n",
    "print(semicolon_separated)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `or` in assignments\n",
    "The return value of `a or b`:\n",
    "* `a` if `a` is truthy\n",
    "* `b` otherwise\n",
    "\n",
    "You can take advantage of this e.g. while writing variable assignments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = 0\n",
    "b = None\n",
    "c = 'John Doe'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color='red'>Instead of doing something like this:</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_variable = 'default value'\n",
    "if a:\n",
    "    my_variable = a\n",
    "elif b:\n",
    "    my_variable = b\n",
    "elif c:\n",
    "    my_variable = c\n",
    "print(my_variable)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### <font color='green'>Prefer doing this:</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_variable = a or b or c or 'default value'\n",
    "print(my_variable)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `try` - `except` - `else`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color='red'>Don't use the following technique for checking if there was exceptions during execution of some block of code.</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "exception_occured = False\n",
    "try:\n",
    "    # here would be the logic of your master piece\n",
    "    \n",
    "    bad_calculation = 1 / 0\n",
    "    \n",
    "except ValueError as e:\n",
    "    print('Oh boi, some value error: {}'.format(e))\n",
    "    exception_occured = True\n",
    "except Exception as e:\n",
    "    print('Oh boi, something bad happened: {}'.format(e))\n",
    "    exception_occured = True\n",
    "    \n",
    "if not exception_occured:\n",
    "    print('All went well!')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### <font color='green'>Use this instead!</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    # here would be the logic of your master piece\n",
    "    \n",
    "    bad_calculation = 1 / 0\n",
    "    \n",
    "except ValueError as e:\n",
    "    print('Oh boi, some keyerror: {}'.format(e))\n",
    "except Exception as e:\n",
    "    print('Oh boi, something bad happened: {}'.format(e))\n",
    "else:\n",
    "    print('All went well!')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `try` - `finally`\n",
    "For scenarios where you want to do something always, even when there are exceptions."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color='red'>Don't do it like this</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def magical_calculation():\n",
    "    try:\n",
    "        # here would be the logic of your master piece\n",
    "        result = 1 / 0\n",
    "    except ZeroDivisionError:\n",
    "        print('This could be something important that should be done every time')\n",
    "        return 0\n",
    "    except Exception:\n",
    "        print('This could be something important that should be done every time')\n",
    "        return None\n",
    "\n",
    "    print('This could be something important that should be done every time')\n",
    "    return result\n",
    "\n",
    "print('return value: {}'.format(magical_calculation()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### <font color='green'>This is better fit for the purpose!</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def magical_calculation():\n",
    "    try:\n",
    "        # here would be the logic of your master piece\n",
    "        result = 1 / 0\n",
    "    except ZeroDivisionError:\n",
    "        return 0\n",
    "    except Exception:\n",
    "        return None\n",
    "    finally:\n",
    "        print('This could be something important that should be done every time')\n",
    "    return result\n",
    "\n",
    "print('return value: {}'.format(magical_calculation()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Note**: You can also have `try`-`except`-`else`-`finally` structure. In cases where exception is not raised inside `try`, `else` will be executed before `finally`. If there is an expection, `else` block is not executed."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Use context managers when possible\n",
    "One use case example is file I/O."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color='red'>Don't play with files like this.</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    some_file = open('tmp.txt', 'w')\n",
    "    print('the file is now open: {}'.format(not some_file.closed))\n",
    "    \n",
    "    # here would be some logic\n",
    " \n",
    "finally:\n",
    "    some_file.close()\n",
    "    print(\"now it's closed: {}\".format(some_file.closed))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### <font color='green'>Use context manager instead!</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open('tmp.txt', 'w') as some_file:\n",
    "    print('the file is now open: {}'.format(not some_file.closed))\n",
    "    \n",
    "    # here would be some logic\n",
    "\n",
    "print(\"now it's closed: {}\".format(some_file.closed))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### <font color='green'>It's also easy to implement one yourself.</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from contextlib import contextmanager\n",
    "\n",
    "@contextmanager\n",
    "def my_context():\n",
    "    print('Entering to my context')\n",
    "    yield\n",
    "    print('Exiting my context')\n",
    "    \n",
    "def do_stuff():\n",
    "    with my_context():\n",
    "        print('Doing stuff')\n",
    "        \n",
    "    print('Doing some stuff outside my context')\n",
    "        \n",
    "do_stuff()  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `min()` & `max()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "secret_data = (1, 2, 5, 99, 8, -9)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color='red'>No need to bake it yourself.</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "max_value = 0\n",
    "for val in secret_data:\n",
    "    if val > max_value:\n",
    "        max_value = val\n",
    "print(max_value)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### <font color='green'>Use builtin functionality instead!</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "max_value = max(secret_data)\n",
    "print(max_value)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `contextlib.suppress` - ignoring exceptions "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color='red'>If there's a potential exception that is ok, don't handle it like this.</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "value = 0\n",
    "try:\n",
    "    value = 1 / 0  # just for demonstrating purposes \n",
    "except ZeroDivisionError:\n",
    "    pass\n",
    "\n",
    "print(value)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### <font color='green'>Do it like this instead!</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from contextlib import suppress\n",
    "\n",
    "value = 0\n",
    "with suppress(ZeroDivisionError):\n",
    "    value = 1 / 0  # just for demonstrating purposes\n",
    "    \n",
    "print(value)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Properties instead of getter/setter methods"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color='red'>Instead of doing something like this.</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Person:\n",
    "    def __init__(self, first_name, last_name):\n",
    "        self.first_name = first_name\n",
    "        self.last_name = last_name\n",
    "        \n",
    "    def get_full_name(self):\n",
    "        return '{} {}'.format(self.first_name, self.last_name)\n",
    "    \n",
    "    def set_full_name(self, full_name):\n",
    "        parts = full_name.split()\n",
    "        if len(parts) != 2:\n",
    "            raise ValueError('Sorry, too difficult name')\n",
    "            \n",
    "        self.first_name, self.last_name = parts \n",
    "        \n",
    "      \n",
    "p = Person('John', 'Doe')\n",
    "print(p.get_full_name())\n",
    "p.set_full_name('Lisa Doe')\n",
    "print(p.get_full_name())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### <font color='green'>Prefer properties!</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Person:\n",
    "    def __init__(self, first_name, last_name):\n",
    "        self.first_name = first_name\n",
    "        self.last_name = last_name\n",
    "        \n",
    "    @property\n",
    "    def full_name(self):\n",
    "        return '{} {}'.format(self.first_name, self.last_name)\n",
    "    \n",
    "    @full_name.setter\n",
    "    def full_name(self, name):\n",
    "        parts = name.split()\n",
    "        if len(parts) != 2:\n",
    "            raise ValueError('Sorry, too difficult name')\n",
    "            \n",
    "        self.first_name, self.last_name = parts\n",
    "\n",
    "    \n",
    "p = Person('John', 'Doe')\n",
    "print(p.full_name)\n",
    "p.full_name = 'Lisa Doe'\n",
    "print(p.full_name)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
